package com.qfxl.common.comm.mybatis;

import javax.persistence.Table;
import java.lang.reflect.ParameterizedType;
import java.util.*;

/**
 * 动态条件构造器
 */
public class QueryWrapper<T> {
    private final List<Condition> conditions = new ArrayList<>();
    private final Map<String, Object> params = new HashMap<>();
    private int paramIndex = 0;
    private final Class<T> entityClass;

    public QueryWrapper(Class<T> entityClass) {
        this.entityClass = entityClass;
    }

    public Class<T> getEntityClass() {
        return entityClass;
    }

    /**
     * AND 条件
     *
     * @param column 字段名
     * @param type   条件类型
     * @param value  值
     * @return 当前对象
     */
    public QueryWrapper<T> and(String column, ConditionType type, Object value) {
        conditions.add(new Condition(column, type, value, "AND"));
        return this;
    }

    public Class<T> getTClass() {
        Class<T> tClass = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
        return tClass;
    }


    public String getTableNameByClazz(Class<?> po) {
        if (po.isAnnotationPresent(Table.class)) {
            return po.getAnnotation(Table.class).name();
        } else {
            String tName = po.getSimpleName();
            if ("po".equals(tName.substring(tName.length() - 3, tName.length() - 1))) {
                tName = tName.substring(0, tName.length() - 3);
            }
            return tName;
        }
    }

    /**
     * OR 条件
     *
     * @param column 字段名
     * @param type   条件类型
     * @param value  值
     * @return 当前对象
     */
    public QueryWrapper<T> or(String column, ConditionType type, Object value) {
        conditions.add(new Condition(column, type, value, "OR"));
        return this;
    }

    /**
     * 构建SQL WHERE条件
     *
     * @return SQL条件字符串
     */
    public String buildSql() {
        if (conditions.isEmpty()) {
            return "";
        }

        StringBuilder sql = new StringBuilder(" WHERE ");
        boolean firstCondition = true;
        params.clear();
        paramIndex = 0;

        for (Condition condition : conditions) {
            if (!firstCondition) {
                sql.append(" ").append(condition.getLogic()).append(" ");
            }

            String paramName = "param" + paramIndex;
            params.put(paramName, condition.getValue());

            switch (condition.getType()) {
                case EQ:
                    sql.append(condition.getColumn()).append(" = #{query.conditions[").append(paramIndex).append("].value}");
                    break;
                case NE:
                    sql.append(condition.getColumn()).append(" != #{query.conditions[").append(paramIndex).append("].value}");
                    break;
                case GT:
                    sql.append(condition.getColumn()).append(" > #{query.conditions[").append(paramIndex).append("].value}");
                    break;
                case GE:
                    sql.append(condition.getColumn()).append(" >= #{query.conditions[").append(paramIndex).append("].value}");
                    break;
                case LT:
                    sql.append(condition.getColumn()).append(" < #{query.conditions[").append(paramIndex).append("].value}");
                    break;
                case LE:
                    sql.append(condition.getColumn()).append(" <= #{query.conditions[").append(paramIndex).append("].value}");
                    break;
                case LIKE:
                    sql.append(condition.getColumn()).append(" LIKE #{query.conditions[").append(paramIndex).append("].value}");
                    break;
                case NOT_LIKE:
                    sql.append(condition.getColumn()).append(" NOT LIKE #{query.conditions[").append(paramIndex).append("].value}");
                    break;
                case IS_NULL:
                    sql.append(condition.getColumn()).append(" IS NULL");
                    break;
                case IS_NOT_NULL:
                    sql.append(condition.getColumn()).append(" IS NOT NULL");
                    break;
                case IN:
                    sql.append(condition.getColumn()).append(" IN ")
                            .append(buildInClause(paramIndex, condition.getValue()));
                    break;
                case NOT_IN:
                    sql.append(condition.getColumn()).append(" NOT IN ")
                            .append(buildInClause(paramIndex, condition.getValue()));
                    break;
            }
            paramIndex++;
            firstCondition = false;
        }

        return sql.toString();
    }

    /**
     * 构建IN条件的值列表部分
     */
    private String buildInClause(int paramIndex, Object value) {
        if (value instanceof Collection) {
            Collection<?> collection = (Collection<?>) value;
            StringBuilder sb = new StringBuilder("(");
            for (int i = 0; i < collection.size(); i++) {
                if (sb.length() > 1) {
                    sb.append(", ");
                }
                sb.append("#{query.conditions[").append(paramIndex).append("].value[").append(i).append("]}");
            }
            sb.append(")");
            return sb.toString();
        } else if (value != null && value.getClass().isArray()) {
            Object[] array = (Object[]) value;
            StringBuilder sb = new StringBuilder("(");
            for (Object item : array) {
                if (sb.length() > 1) {
                    sb.append(", ");
                }
                sb.append("#{item}");
            }
            sb.append(")");
            return sb.toString();
        }
        throw new IllegalArgumentException("IN操作符的值必须是Collection或数组类型");
    }

    /**
     * 获取参数Map
     *
     * @return 参数Map
     */
    public Map<String, Object> getParams() {
        return params;
    }

    /**
     * 条件类型枚举
     */
    public enum ConditionType {
        EQ, NE, GT, GE, LT, LE, LIKE, NOT_LIKE, IS_NULL, IS_NOT_NULL, IN, NOT_IN
    }

    /**
     * 条件内部类
     */
    private static class Condition {
        private final String column;
        private final ConditionType type;
        private final Object value;
        private final String logic;

        public Condition(String column, ConditionType type, Object value, String logic) {
            this.column = column;
            this.type = type;
            this.value = value;
            this.logic = logic;
        }

        public String getColumn() {
            return column;
        }

        public ConditionType getType() {
            return type;
        }

        public Object getValue() {
            return value;
        }

        public String getLogic() {
            return logic;
        }
    }
}